package com.fliad.ai.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.fliad.ai.core.ChatSessionImpl;
import com.fliad.ai.entity.AiAgent;
import com.fliad.ai.entity.AiLlm;
import com.fliad.ai.service.AiAgentService;
import com.fliad.ai.service.AiLlmService;
import lombok.extern.slf4j.Slf4j;
import org.noear.snack.ONode;
import org.noear.solon.ai.chat.ChatConfig;
import org.noear.solon.ai.chat.ChatModel;
import org.noear.solon.ai.chat.ChatSession;
import org.noear.solon.ai.chat.message.ChatMessage;
import org.noear.solon.annotation.*;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Result;
import org.noear.solon.core.util.MimeType;
import reactor.core.publisher.Flux;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;

/**
 * @author wyl
 * @date 2025年07月10日 21:22
 */
@Slf4j
@Controller
public class AiAgentController {

    @Inject
    AiAgentService aiAgentService;
    @Inject
    AiLlmService aiLlmService;
    // 聊天会话 @todo 后续session id从前端传递，为每个session id创建一个会话
    ChatSession chatSession = new ChatSessionImpl("session-1"); //安排个会话id

    // 创建
    @Post
    @Mapping("")
    public Result<Boolean> create(@Body AiAgent agent) throws SQLException {
        agent.setId(IdUtil.getSnowflakeNextId());
        agent.setCreated(new Date());
        agent.setModified(new Date());
        return Result.succeed(aiAgentService.create(agent));
    }

    // 查询单个
    @Get
    @Mapping("/{id}")
    public Result<AiAgent> get(@Path("id") Long id) throws SQLException {
        AiAgent agent = aiAgentService.get(id);
        return Result.succeed(agent);
    }

    // 查询列表
    @Get
    @Mapping("")
    public Result<List<AiAgent>> list() throws SQLException {
        return Result.succeed(aiAgentService.list());
    }

    // 更新
    @Post
    @Mapping("/{id}")
    public Result<Boolean> update(@Path("id") Long id, @Body AiAgent agent) throws SQLException, IllegalAccessException {
        agent.setModified(new Date());
        return Result.succeed(aiAgentService.update(id, agent));
    }

    // 删除
    @Delete
    @Mapping("/{id}")
    public Result<Boolean> delete(@Path("id") Long id) throws SQLException {
        return Result.succeed(aiAgentService.delete(id));
    }

    @Post
    @Mapping("/chat/{id}")
    public void chat(@Path("id") Long id, ONode userMsg, Context ctx) throws SQLException {
        AiAgent agent = aiAgentService.get(id);
        AiLlm llm = aiLlmService.get(agent.getLlmId());
        ChatConfig chatConfig = new ChatConfig();
        if (llm != null) {
            chatConfig.setApiKey(llm.getLlmApiKey());
            chatConfig.setApiUrl(llm.getLlmEndpoint());
            chatConfig.setProvider(llm.getDialect());
            chatConfig.setModel(llm.getLlmModel());
        }
        ChatModel.Builder chatModelBuilder = ChatModel.of(chatConfig);
        ChatModel chatModel = chatModelBuilder.build();

        ctx.contentType(MimeType.TEXT_EVENT_STREAM_VALUE);

        if (StrUtil.isNotBlank(agent.getOptions().get("systemPrompt").getString())) {
            // 设置系统消息
            chatSession.addMessage(ChatMessage.ofSystem(agent.getOptions().get("systemPrompt").getString()));
        }
        chatSession.addMessage(ChatMessage.ofUser(userMsg.get("messages").get(0).get("content").getString()));
        Flux.from(chatModel
                        .prompt(chatSession)
                        .options(chatOptions -> {
                            // 设置参数 @todo
                            // 知识库转化为工具
                            /*chatOptions.toolsAdd(new FunctionToolDesc("").description("").doHandle(args -> {
                                return "{}";
                            }));
                            // 工作流转工具
                            chatOptions.toolsAdd(new FunctionToolDesc("").description("").doHandle(args -> {
                                return "{}";
                            }));*/
                        })
                        .stream())
                .filter(resp -> resp.hasChoices())
                .doOnNext(resp -> {
                    try {
                        ctx.render(resp.getMessage());
                        ctx.output("\n");
                        ctx.flush();
                    } catch (Throwable ex) {
                        throw new RuntimeException(ex);
                    }
                }).doOnError(err -> {
                    //
                }).then().block();
    }

}
